﻿using System.Data;
using AngleSharp.Css.Dom;
using AutoMapper;
using Common.ORM.Repository;
using sixgod.Common.Exception;
using sixgod.Common.Helper;
using sixgod.Common.Model;
using sixgod.Dtos.WorkFlow;
using sixgod.Dtos.WorkFlow.Query;
using sixgod.Enum;
using sixgod.Model.Attribute;
using sixgod.Model.WorkFlow;
using sixgod.Service.Dictionary;
using sixgod.Service.User;
using sixgod.Service.WorkFlowTemplate;
using SqlSugar;

namespace sixgod.Service.WorkFlow.Implementation;

[Di(typeof(IWorkFlowService))]
public class WorkFlowService : Repository<Model.WorkFlow.WorkFlow>, IWorkFlowService
{
    private readonly IDepartmentService _departmentService;
    private readonly IDictionaryService _dictionaryService;
    private readonly IJobService _jobService;
    private readonly IMapper _mapper;
    private readonly IWorkFlowTemplateService _templateService;

    public WorkFlowService(ISqlSugarClient context, IMapper mapper, IWorkFlowTemplateService templateService, IDictionaryService dictionaryService, IJobService jobService, IDepartmentService departmentService) : base(context)
    {
        _mapper = mapper;
        _templateService = templateService;
        _dictionaryService = dictionaryService;
        _jobService = jobService;
        _departmentService = departmentService;
    }


    /// <summary>
    /// 分页数据
    /// </summary>
    /// <param name="query"></param>
    /// <returns></returns>
    public async Task<PageResponseResult<WorkFlowDto>> GetPageList(WorkFlowQuery query)
    {
        RefAsync<int> total = 0;
        var list = await Context.Queryable<Model.WorkFlow.WorkFlow>().Includes(r => r.CurrentNode).Includes(r => r.InitUser).Includes(r => r.RebackUser)
            // .LeftJoin<WorkFlowNode>((w,n)=>w.Id==n.WorkFlowId)
            .Where(r => r.IsDelete == 0)
            .WhereIF(query.StartTime != null, r => r.InitTime >= query.StartTime)
            .WhereIF(query.EndTime != null, r => r.InitTime <= query.EndTime).WhereIF(query.Result != null, r => r.Result == query.Result)
            .OrderByDescending(r=>r.UpdateTime)
            .ToPageListAsync(query.PageNum, query.PageSize, total);

        // var levelDic = await _dictionaryService.GetDicItemList("WorkFlowLevel");
        var typeDic = await _dictionaryService.GetDicItemList("WorkFlowType");

        var data = _mapper.Map<List<WorkFlowDto>>(list);
        data.ForEach(r => r.Type = typeDic.First(e => e.Value == r.Type).Text);

        PageResponseResult<WorkFlowDto> result = new PageResponseResult<WorkFlowDto>
        {
            Count = total.Value,
            Data = data
        };

        return result;
    }

    /// <summary>
    /// 发起流程
    /// </summary>
    /// <param name="data"></param>
    /// <returns></returns>
    public async Task StartWorkFlow(StartWorkFlowInDto data)
    {
        // 1.判断是否有权限发起
        var flow = await _templateService.GetByIdAsync(data.TemplateId);
        if (!await checkHavePermissionStartWorkFlow(flow))
        {
            throw new WorkFlowException("没有发起该流程的权限");
        }

        // 2.发起流程
        // 获取流程所有信息

        var flowInfo = await _templateService.GetTemplateInfos(data.TemplateId);
        List<WorkFlowNode> nodes;
        List<NodeLink> links;
        List<AuditPerson> auditPersons = new List<AuditPerson>();
        try
        {
            nodes = _mapper.Map<List<WorkFlowNode>>(flowInfo.Item1);
            links = _mapper.Map<List<NodeLink>>(flowInfo.Item2);
        }
        catch (Exception e)
        {
            throw new WorkFlowException("类型转化错误：", e);
        }

        Model.WorkFlow.WorkFlow workFlow = new Model.WorkFlow.WorkFlow();
        workFlow.Create("-1", flow);

        // 流程表单
        var currentUserId = UserContext.Current.UserToken.UserId;

        foreach (var item in data.FormItems)
        {
            item.Create(currentUserId);
            item.WorkFlowId = workFlow.Id;
        }


        // 替换节点的ID
        foreach (var r in nodes)
        {
            r.WorkFlowId = workFlow.Id;
            var oldId = r.Id;
            r.Create("admin");

            // 添加审核人节点
            if (r.AddDepartmentLeader)
            {
                var auditer = await _departmentService.GetDepartmentLeader(UserContext.Current.UserToken.DepartMentId);
                var temp = new AuditPerson(workFlow.Id, r.Id, auditer.Id, currentUserId);
                auditPersons.Add(temp);
            }

            if (r.AddJobLeader)
            {
                var auditer = await _jobService.GetJobLeader();
                var temp = new AuditPerson(workFlow.Id, r.Id, auditer.Id, currentUserId);
                auditPersons.Add(temp);
            }

            // 指定任何人或抄送人
            var pointAuditer = await _templateService.GetNodePointAuditer(oldId);
            pointAuditer.ForEach(e =>
            {
                var temp = new AuditPerson(workFlow.Id, r.Id, e.AuditerCode, currentUserId);
                auditPersons.Add(temp);
            });

            links.ForEach(e =>
            {
                if (e.NextNode == oldId)
                {
                    e.NextNode = r.Id;
                }

                if (e.LastNode == oldId)
                {
                    e.LastNode = r.Id;
                }

                e.WorkFlowId = workFlow.Id;
                e.Create("admin");
            });
        }

        
       

        // 当前节点设置为开始节点，
        var startNodeId = nodes.First(r => r.EnumNodeType == EnumNodeType.Start).Id;
        workFlow.CurrentNodeId = startNodeId;
        //写入数据库
        try
        {
            // 设置数据库事务隔离级别
            await Context.Ado.BeginTranAsync(IsolationLevel.ReadUncommitted);

            await Context.Insertable(workFlow).ExecuteCommandAsync();
            await Context.Insertable(data.FormItems).SplitTable().ExecuteCommandAsync();
            await Context.Insertable(nodes).ExecuteCommandAsync();
            await Context.Insertable(links).ExecuteCommandAsync();
            await Context.Insertable(auditPersons).ExecuteCommandAsync();
            await FlowNext(workFlow.Id,EnumNodeOperation.Start);

            await Context.Ado.CommitTranAsync();
        }
        catch (Exception e)
        {
            await Context.Ado.RollbackTranAsync();
            throw;
        }
    }

    /// <summary>
    /// 获取流程详细
    /// </summary>
    /// <param name="id">流程Id</param>
    /// <returns></returns>
    public async Task<WorkFlowDetailDto> GetWorkFlowDetailDto(string id)
    {
        var data = new WorkFlowDetailDto();
        // 节点信息
        var nodeList = await getDetailNodeInfo(id);
        var currentNode = await Context.Queryable<Model.WorkFlow.WorkFlow>().Where(r=>r.Id == id).Select(r=>r.CurrentNodeId).FirstAsync();
        data.Nodes = nodeList.Select(r=>new WorkFlowNodeDto(){Id = r.Id, Name = r.Name,NodeType = (int)r.EnumNodeType}).ToList();
        
        var audits = await Context.Queryable<AuditPerson>().Includes(r=>r.Person).Where(r=>r.WorkFlowId == id).ToListAsync();
        data.Nodes.ForEach(r=>
        {
            var tempList = audits.Where(e=>e.NodeId == r.Id).ToList();
            r.AuditInfoList = _mapper.Map<List<AuditInfo>>(tempList);
            if (r.Id == currentNode)
            {
                r.IsCurrentNode = true;
            }
        });

        // 表单信息
        var tableName = Context.SplitHelper<FormItem>().GetTableName(nodeList[0].CreateTime);
        var formItems = await Context.Queryable<FormItem>().AS(tableName).Where(r=>r.WorkFlowId == id).ToListAsync();
        data.FormItems = _mapper.Map<List<FormItemDto>>(formItems);
        return data;
    }

    /// <summary>
    /// 审核流程
    /// </summary>
    /// <param name="data"></param>
    /// <returns></returns>
    public async Task AuditFlow(FlowExecuteInDto data)
    {
        try
        {
            if (data.Operation == EnumNodeOperation.Reject)
            {
                await Context.Ado.BeginTranAsync(IsolationLevel.ReadUncommitted);
            }
            else
            {
                await Context.Ado.BeginTranAsync();
            }
            
            await FlowNext(data.WorkFlowId,data.Operation,data.Opinion);
            await Context.Ado.CommitTranAsync();
        }
        catch (Exception e)
        {
            await Context.Ado.RollbackTranAsync();
            throw;
        }
    }

    /// <summary>
    /// 根据ID获取表单元素
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    public async Task<List<FormItem>> GetFormItems(string id)
    {
        var tableName = Context.SplitHelper<FormItem>().GetTableName((await GetByIdAsync(id)).CreateTime);
        var query = Context.Queryable<FormItem>().AS(tableName).Where(r=>r.IsDelete == 0 && r.WorkFlowId == id);
        return await query.ToListAsync();
    }

    /// <summary>
    /// 编辑，重新开始流程
    /// </summary>
    /// <param name="data"></param>
    /// <returns></returns>
    public async Task ReStartWorkFlow(StartWorkFlowInDto data)
    {
        try
        {
            var workflow = await GetByIdAsync(data.TemplateId);
            var formItems = await GetFormItems(data.TemplateId);
            foreach (var item in formItems)
            {
                var temp = data.FormItems.FirstOrDefault(r=>r.Id== item.Id);
                if (temp == null)
                {
                    throw new WorkFlowException("找不到对应表单项的ID");
                }
                item.Value = temp.Value;
            }
            await Context.Ado.BeginTranAsync();

            await Context.Updateable(formItems).SplitTable().ExecuteCommandAsync();
            await FlowNext(workflow.Id,EnumNodeOperation.Start);

            await Context.Ado.CommitTranAsync();
        }
        catch (Exception e)
        {
            await Context.Ado.RollbackTranAsync();
            throw;
        }
        
    }

    /// <summary>
    /// 获取详情页面流程节点信息（有序）
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    private async Task<List<WorkFlowNode>> getDetailNodeInfo(string id)
    {
        var link = await Context.Queryable<NodeLink>().Where(r => r.WorkFlowId == id).ToListAsync();
        var nodes = await Context.Queryable<WorkFlowNode>().Where(r=>r.WorkFlowId == id).ToListAsync();
        var startNode = nodes.First(r=>r.EnumNodeType == EnumNodeType.Start);
        var result = new List<WorkFlowNode>
        {
            startNode
        };
        recursionGetNode(startNode,link,nodes,ref result);
        result.Add(nodes.FirstOrDefault(e=>e.EnumNodeType==EnumNodeType.End));
        return result;
    }

    /// <summary>
    /// 递归获取节点（有序）
    /// </summary>
    /// <param name="currentNode"></param>
    /// <param name="links"></param>
    /// <param name="nodes"></param>
    /// <param name="result"></param>
    private void recursionGetNode(WorkFlowNode currentNode, IEnumerable<NodeLink> links, IEnumerable<WorkFlowNode> nodes, ref List<WorkFlowNode> result)
    {
        var nextLinks = links.Where(r=>r.LastNode == currentNode.Id && r.Operation is not EnumNodeOperation.Reject);
        var nextNodeIds = nextLinks.Select(e=>e.NextNode);
        var nextNodes = nodes.Where(r=> nextNodeIds.Any(e=>e==r.Id));

        if (nextNodes.Any(r=>r.EnumNodeType == EnumNodeType.CarbonCopy))
        {
            result.Add(nextNodes.First(r=>r.EnumNodeType == EnumNodeType.CarbonCopy));
        }

        if (nextNodes.Any(r=>r.EnumNodeType == EnumNodeType.Audit))
        {
            var temp = nextNodes.First(r=>r.EnumNodeType == EnumNodeType.Audit);
            result.Add(temp);
            recursionGetNode(temp,links,nodes,ref result);
        }
    }


    /// <summary>
    /// 执行流程
    /// 最好在事务内运行
    /// </summary>
    /// <param name="workFlowId"></param>
    /// <param name="operation"></param>
    /// <param name="opinion">意见</param>
    /// <returns></returns>
    /// <exception cref="WorkFlowException"></exception>
    private async Task FlowNext(string workFlowId, EnumNodeOperation operation, string opinion = null)
    {
        var workFlow = await Context.Queryable<Model.WorkFlow.WorkFlow>().Includes(r => r.CurrentNode).FirstAsync(r => r.Id == workFlowId);
        var currentNode = workFlow.CurrentNode;
        var links = await Context.Queryable<NodeLink>().Where(r => r.WorkFlowId == workFlowId && r.LastNode == workFlow.CurrentNode.Id).ToListAsync();
        if (links.Count == 0 && operation != EnumNodeOperation.End)
        {
            throw new WorkFlowException("流程错误！在非结束的节点没有后续操作！");
        }

        var currentUserId = UserContext.Current.UserToken.UserId;
        if (operation == EnumNodeOperation.Start && currentNode.EnumNodeType == EnumNodeType.Start)
        {
            if (links.Count > 1)
            {
                throw new WorkFlowException("开始节点下存在两个节点！");
            }

            var node = await GetNodeById(links.First().NextNode);
            AuditPerson startPerson = new AuditPerson(workFlowId, currentNode.Id, currentUserId, currentUserId);
            await Context.Insertable(startPerson).ExecuteCommandAsync();
            workFlow.CurrentNodeId = node.Id;
            workFlow.Update(currentUserId);
            workFlow.Result = 0;
            await Context.Updateable(workFlow).ExecuteCommandAsync();

            var history = new WorkFlowHistory();
            history.Create(currentUserId,links.First().Id,workFlow.Id);
            await Context.Insertable(history).SplitTable().ExecuteCommandAsync();

            return;
        }

        async Task SaveAuditResult(EnumNodeOperation result, AuditPerson auditor)
        {
            auditor.Opinion = opinion;
            auditor.Result = result;
            auditor.AuditTime = DateTime.Now;
            auditor.Update(currentUserId);
            await Context.Updateable(auditor).ExecuteCommandAsync();
        }


        void Verify(AuditPerson auditor)
        {
           
            if (auditor == null)
            {
                throw new WorkFlowException("该人员没有审批权限！");
            }

            if (auditor.Result != null)
            {
                throw new WorkFlowException("不能重复审核节点");
            }
            
        }

        if (operation == EnumNodeOperation.Agree && currentNode.EnumNodeType == EnumNodeType.Audit)
        {
            var auditPersons = await getNodeAuditor(currentNode.Id);
            var auditor = auditPersons.FirstOrDefault(r => r.UserCode == currentUserId);
            Verify(auditor);

            var agreeLink = links.Where(r=>r.Operation == EnumNodeOperation.Agree).ToList();
            if (!agreeLink.Any())
            {
                throw new WorkFlowException("流程不完整，没有同意的路径！");
            }

            // 流程流转
            async Task FlowMove(NodeLink nextLink,bool reject = false)
            {
                var nextNode = await GetNodeById(nextLink.NextNode);

                if (nextNode.EnumNodeType is EnumNodeType.Audit or EnumNodeType.End)
                {
                    if (nextNode.EnumNodeType == EnumNodeType.End)
                    {
                        workFlow.Result = reject ? 2 : 1;
                    }

                    workFlow.CurrentNodeId = nextLink.NextNode;
                    workFlow.Update(currentUserId);
                    await Context.Updateable(workFlow).ExecuteCommandAsync();

                    var history = new WorkFlowHistory();
                    history.Create(currentUserId,nextLink.Id,workFlow.Id);
                    await Context.Insertable(history).SplitTable().ExecuteCommandAsync();

                    currentNode.AuditTime = DateTime.Now;
                    currentNode.Update(currentUserId);
                    await Context.Updateable(currentNode).ExecuteCommandAsync();
                }

                if (nextNode.EnumNodeType == EnumNodeType.CarbonCopy)
                {
                    // todo:抄送

                }
                
            }

            foreach (var nextLink in agreeLink)
            {
                if (currentNode.EnumAuditMode == EnumAuditMode.OnlyOne)
                {
                    // 一个通过即通过
                    await SaveAuditResult(EnumNodeOperation.Agree,auditor);
                    await FlowMove(nextLink);
                    if (currentNode.SendOther.HasValue && currentNode.SendOther.Value)
                    {
                        // todo:抄送其他人
                    }
                    return;
                }

                // 全部通过才通过
                if (auditPersons.Count(r => r.Result == EnumNodeOperation.Agree) == auditPersons.Count - 1 && auditPersons.First(r => r.Result != EnumNodeOperation.Agree).UserCode == currentUserId)
                {
                    // 当前是最后一个待审核用户，同意后进行流程流转
                    await FlowMove(nextLink);
                }

                // 不确定是否为引用类型，影响流转判断
                await SaveAuditResult(EnumNodeOperation.Agree,auditor);
                return;
            }
        }

        if (operation == EnumNodeOperation.Reject && currentNode.EnumNodeType == EnumNodeType.Audit)
        {
            var auditPersons = await getNodeAuditor(currentNode.Id);
            var auditor = auditPersons.FirstOrDefault(r => r.UserCode == currentUserId);
            Verify(auditor);

            // 保存审批意见
            await SaveAuditResult(EnumNodeOperation.Reject,auditor);
            
            // 流程流转
            if (currentNode.RejectOperation == null || currentNode.RejectOperation == "2")
            {
                // 为空则直接结束
                workFlow.Result = 2;
                workFlow.Update(currentUserId);
                workFlow.CurrentNodeId = await Context.Queryable<WorkFlowNode>().Where(r=>r.WorkFlowId == workFlowId && r.EnumNodeType == EnumNodeType.End && r.IsDelete == 0)
                    .Select(r=>r.Id).FirstAsync();
                await Context.Updateable(workFlow).ExecuteCommandAsync();
                // todo 流转记录

            }
            else if (currentNode.RejectOperation == "1")
            {
                // 回退
                var rejectLink = links.FirstOrDefault(r=>r.Operation == EnumNodeOperation.Reject);
                if (rejectLink == null)
                {
                    throw new WorkFlowException("没有找到回退节点");
                }

                var nextNode = await GetNodeById(rejectLink.NextNode);
                await clearAuditInfo(workFlow.Id);
                workFlow.CurrentNodeId = nextNode.Id;

                // 回退备注
                workFlow.ReBack(opinion,currentUserId);
                workFlow.Update(currentUserId);
                await Context.Updateable(workFlow).ExecuteCommandAsync();
                
                //保存流转记录
                var history = new WorkFlowHistory();
                history.Create(currentUserId,rejectLink.Id,workFlow.Id);
                await Context.Insertable(history).SplitTable().ExecuteCommandAsync();
            }
            else
            {
                throw new WorkFlowException("流程拒绝后的操作代码异常，代码为："+currentNode.RejectOperation +"\n节点ID："+currentNode.Id);
            }
        }
    }

    /// <summary>
    /// 递归清除审核意见及结果
    /// </summary>
    /// <param name="node"></param>
    /// <returns></returns>
    private async Task clearAuditInfo(WorkFlowNode node)
    {
        if (node.EnumNodeType == EnumNodeType.Audit)
        {
            await Context.Updateable<AuditPerson>().SetColumns(r=>new AuditPerson(){ Result = null,Opinion = null, AuditTime = null})
                .Where(r=>r.NodeId == node.Id).ExecuteCommandAsync();
            var links = await Context.Queryable<NodeLink>().Where(r=>r.LastNode == node.Id).ToListAsync();
            foreach (var nodeLink in links)
            {
                var nextNode = await GetNodeById(nodeLink.NextNode);
                await clearAuditInfo(nextNode);
            }
        }
    }

    private async Task clearAuditInfo(string flowId)
    {
        // TODO：ORM 似乎存在bug 设置Result为null会报错

        var sql = $"update auditperson set result = null,audittime=null,opinion=null where workflowid = '{flowId}'";
        await Context.Ado.ExecuteCommandAsync(sql);
        // await Context.Updateable<AuditPerson>()
        //     .SetColumns("result",null)
        //     .SetColumns("opinion",null)
        //     .SetColumns("audittime",null)
        //     .Where(r=>r.WorkFlowId == flowId)
        //     .ExecuteCommandAsync();
        // await Context.Updateable<AuditPerson>().SetColumns(r=>new AuditPerson(){ Result = null ,Opinion = "", AuditTime = null})
        //         .Where(r=>r.WorkFlowId == flowId).ExecuteCommandAsync();
    }

    /// <summary>
    /// 获取节点的审核人
    /// </summary>
    /// <param name="nodeId"></param>
    /// <returns></returns>
    private Task<List<AuditPerson>> getNodeAuditor(string nodeId)
    {
        return Context.Queryable<AuditPerson>().Where(r => r.NodeId == nodeId).ToListAsync();
    }

    private Task<WorkFlowNode> GetNodeById(string id)
    {
        return Context.Queryable<WorkFlowNode>().FirstAsync(r => r.Id == id);
    }

    /// <summary>
    /// 检查是否有权限发起流程
    /// </summary>
    /// <returns></returns>
    // ReSharper disable once InconsistentNaming
    private async Task<bool> checkHavePermissionStartWorkFlow(Model.WorkFlowTemplate.WorkFlowTemplate template)
    {
        if (template == null)
        {
            throw new WorkFlowException("流程不存在，流程ID：" + template.Id);
        }

        if (template.AnyOneCanStart)
        {
            return true;
        }

        var startInfo = await _templateService.GetWorkFlowStartInfo(template.Id);
        if (startInfo.Count == 0)
        {
            throw new WorkFlowException("该流程无人可发起，请设置权限，流程ID：" + template.Id);
        }

        var departments = startInfo.Where(r => r.Type == EnumStartType.Department).ToList();
        var jobs = startInfo.Where(r => r.Type == EnumStartType.Job).ToList();
        var users = startInfo.Where(r => r.Type == EnumStartType.User).ToList();

        if (departments.Any(r => r.Code == UserContext.Current.UserToken.DepartMentId))
        {
            return true;
        }

        if (jobs.Any(r => r.Code == UserContext.Current.UserToken.JobId))
        {
            return true;
        }

        if (users.Any(r => r.Code == UserContext.Current.UserToken.UserId))
        {
            return true;
        }

        // todo 登录信息未完善 统一返回true
        return true;
    }
}